﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using DimensionsHelper.Common.Contracts.Metadata;

namespace DimensionsHelper.Services.Metadata;

public class VersionObject : IVersionObject
{
    private readonly Dictionary<string, string> _propertiesValueMap = new();

    internal VersionObject(string name = "")
    {
        Operation = VersionOperations.None;
        Index = -1;
        Name = name;
        Data = null;
        TargetObject = null;
        ItemName = null;
    }

    private VersionObject(object data, string name = "") : this(name)
    {
        Data = data;
    }

    internal VersionObject(object data, VersionOperations operation, int index, string name = "")
        : this(data, name)
    {
        Operation = operation;
        Index = index;
    }


    internal object? TargetObject { get; set; }
        
    public VersionOperations Operation { get; internal set; }

    public int Index { get; internal set; }

    public object? Data { get; internal set; }

    public string? ItemName { get; internal set; }

    public string Name { get; internal set; }

    public bool IsSynchronized { get; private set; }

    public void Synchronize()
    {
        if (IsSynchronized || Data == null)
        {
            return;
        }

        if (Data == null)
        {
            UpdateObjectPropertyValue();
        }
        else if (TargetObject is MetadataObject metadataObject)
        {
            metadataObject.SetVersionObjectData(this);
        }

        IsSynchronized = true;
    }

    internal void SetPropertyValue(string propertyName, string value)
    {
        if (!_propertiesValueMap.TryAdd(propertyName, value))
        {
            _propertiesValueMap[propertyName] = value;
        }
    }

    private void UpdateObjectPropertyValue()
    {
        if (TargetObject == null || IsSynchronized)
        {
            return;
        }

        var type = TargetObject.GetType();

        foreach (var pair in _propertiesValueMap)
        {
            var key = pair.Key;
            var value = pair.Value;
            var prop = type.GetProperty(key, BindingFlags.IgnoreCase);

            if (prop == null)
            {
                continue;
            }

            if (prop.PropertyType == typeof(string))
            {
                prop.SetValue(TargetObject, value);
            }
            else if (prop.PropertyType == typeof(int) && int.TryParse(value, out var iValue))
            {
                prop.SetValue(TargetObject, iValue);
            }
            else if (prop.PropertyType == typeof(float) && float.TryParse(value, out var fValue))
            {
                prop.SetValue(TargetObject, fValue);
            }
            else if (prop.PropertyType == typeof(double) && double.TryParse(value, out var dValue))
            {
                prop.SetValue(TargetObject, dValue);
            }
            else if (prop.PropertyType == typeof(bool))
            {
                prop.SetValue(TargetObject, value == "-1");
            }
            else if (prop.PropertyType.IsEnum)
            {
                prop.SetValue(TargetObject,
                    int.TryParse(value, out var enumValue)
                        ? Enum.ToObject(prop.PropertyType, enumValue)
                        : Enum.Parse(prop.PropertyType, value));
            }
        }
    }
}


public class VersionObjectCollection : IVersionObjectCollection
{
    private readonly List<VersionObject> _items;

    internal VersionObjectCollection(object target, IMetadataDocument document)
    {
        _items = new List<VersionObject>();
        TargetObject = target;
        Document = document;
    }

    public IVersionObject this[int index] => _items[index];

    public IMetadataDocument Document { get; }

    public int Count => _items.Count;

    public object TargetObject { get; internal set; }


    public IVersionObject AddNew(string name = "")
    {
        var obj = new VersionObject(name)
        {
            TargetObject = TargetObject
        };
        _items.Add(obj);
        return obj;
    }

    public IVersionObject AddNew(object data, VersionOperations operation, int index, string name = "")
    {
        var obj = new VersionObject(data, operation, index, name)
        {
            TargetObject = TargetObject
        };
        _items.Add(obj);
        return obj;
    }


    #region IEnumerable

    public IEnumerator<IVersionObject> GetEnumerator()
    {
        return _items.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }

    #endregion
}


public class Version : MetadataNamedObject, IVersion
{
    private readonly List<IVersionObject> _items;

    internal Version(IMetadataDocument document, string name, string id) :
        base(document, ObjectType.Version, name)
    {
        _items = [];

        Id = id;
        Label = string.Empty;
        Description = string.Empty;
        CreateBy = string.Empty;
        Major = string.Empty;
        Minor = string.Empty;
        MdmVersion = null;
        IsLocked = false;
        DateCreated = DateTime.Now;
        DateLocked = DateTime.Now;
        IsSynchronized = false;

        SubVersions = new VersionList(document);
    }


    public string Id { get; internal set; }

    public string Label { get; internal set; }

    public string Description { get; internal set; }

    public string CreateBy { get; internal set; }

    public string Major { get; internal set; }

    public string Minor { get; internal set; }

    public string? MdmVersion { get; internal set; }

    public IVersionList SubVersions { get; }

    public bool IsLocked { get; private set; }

    public DateTime DateCreated { get; internal set; }

    public DateTime DateLocked { get; internal set; }

    public IReadOnlyList<IVersionObject> VersionObjectSet => _items;

    public bool IsSynchronized { get; private set; }


    public void Add(IVersionObject obj)
    {
        _items.Add(obj);
    }


    public void Lock()
    {
        IsLocked = true;
    }


    public void Synchronize()
    {
        if (IsSynchronized)
        {
            return;
        }

        foreach (var obj in VersionObjectSet)
        {
            obj.Synchronize();
        }

        IsSynchronized = true;
    }
}


public class VersionList : MetadataStringKeyCollection<IVersion>, IVersionList
{
    internal VersionList(IMetadataDocument document)
    {
        Document = document;
        ObjectType = ObjectType.Versions;
        Properties = new Properties(document);
        Latest = null;
        Versioned = true;
    }


    public IVersion? Latest { get; private set; }


    #region IMetadataObject

    public IMetadataDocument Document { get; }

    public ObjectType ObjectType { get; }

    public IMetadataObject? Parent { get; internal set; }

    public IProperties Properties { get; }

    /// <summary>
    ///     此属性固定是 null
    /// </summary>
    public IVersionObjectCollection? Versions => null;

    public bool Versioned { get; set; }

    #endregion


    #region MetadataNamedObjectCollection<IVersion>

    protected override string KeySelector(IVersion item)
    {
        return item.Name;
    }

    public override IVersion CreateObject(string name)
    {
        return new Version(Document, name, Guid.NewGuid().ToString());
    }

    public override void Clear()
    {
        base.Clear();
        Latest = null;
    }

    public T? FindParent<T>() where T : IMetadataObject
    {
        return MetadataObject.FindParent<T>(this);
    }

    #endregion
}